﻿/*
 * This file is part of MonoStrategy.
 *
 * Copyright (C) 2010-2011 Christoph Husse
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Authors: 
 *      # Christoph Husse
 * 
 * Also checkout our homepage: http://monostrategy.codeplex.com/
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace MonoStrategy
{
    public class PlantProduceBuilding : OutputOnlyBuilding, IBuildingWithWorkingArea
    {
        private FoilageType[] m_PlantingTypes;

        internal FoilageType FoilageCuttingMask { get; private set; }
        internal FoilageType FoilagePlantingMask { get; private set; }

        private static Resource GetResourceParameter(String inParameter)
        {
            return (Resource)Enum.Parse(typeof(Resource), inParameter.Split(':')[0]);
        }

        private static int GetRadiusParameter(String inParameter)
        {
            return Int32.Parse(inParameter.Split(':')[1]);
        }

        internal PlantProduceBuilding(BuildingManager inParent, BuildingConfig inConfig, Point inPosition, String inParameter)
            : this(inParent, inConfig, inPosition, GetResourceParameter(inParameter), GetRadiusParameter(inParameter))
        {
        }

        internal PlantProduceBuilding(BuildingManager inParent, BuildingConfig inConfig, Point inPosition, Resource inResource, int inSearchRadius)
            : base(inParent, inConfig, inPosition)
        {
            if ((inSearchRadius < 2) || (inSearchRadius > 50))
                throw new ArgumentOutOfRangeException();

            WorkingRadius = inSearchRadius;
            WorkingArea = SpawnPoint.Value.ToPoint();

            switch (inResource)
            {
                case Resource.Wood:
                    {
                        FoilageCuttingMask = FoilageType.Tree1;
                        FoilagePlantingMask = FoilageType.Tree1;
                    }break;

                case Resource.Wine:
                    {
                        FoilageCuttingMask = FoilageType.Wine;
                        FoilagePlantingMask = FoilageType.Wine;
                    }break;

                case Resource.Grain:
                    {
                        FoilageCuttingMask = FoilageType.Grain;
                        FoilagePlantingMask = FoilageType.Grain;
                    }break;

                default:
                    throw new ArgumentOutOfRangeException();
            }

            List<FoilageType> plantingTypes = new List<FoilageType>();

            foreach (var value in Enum.GetValues(typeof(FoilageType)))
            {
                FoilageType bit = (FoilageType)value;

                if (bit.BitCount() != 1)
                    continue;

                if (FoilagePlantingMask.IsSet(bit))
                    plantingTypes.Add(bit);
            }

            m_PlantingTypes = plantingTypes.ToArray();
        }

        protected override bool Produce(Procedure<bool> onCompleted)
        {
            // search for grown foialge of the given type
            Foilage foilage = Parent.ResMgr.FindFoilageAround(WorkingArea, WorkingRadius, FoilageCuttingMask, FoilageState.Grown);

            if (foilage == null)
                return false;

            // cut foilage
            JobFoilageCutting job = new JobFoilageCutting(SpawnWorker(), foilage, this);

            job.OnCompleted += (unused, succeeded) => { onCompleted(succeeded); };
            WorkerOrNull.Job = job;

            return true;
        }

        protected override bool Plant(Procedure onCompleted)
        {
            /*
             * In theory we could use a complex algorithm to determine a near optimal balance between
             * planting and producing but since we need a new foilage for every cut foilage, the following
             * trivial random assumption proves to be sufficient and looks nearly optimal for most scenarios...
             */
            if (m_Random.NextDouble() < 0.5)
                return false;

            // search for free space
            FoilageType plantingType = m_PlantingTypes[m_Random.Next(0, m_PlantingTypes.Length)];
            Point foilagePos = new Point();

            if (WalkResult.Success != GridSearch.GridWalkAround(WorkingArea, Parent.Terrain.Size, Parent.Terrain.Size, (pos) =>
                    {
                        if ((Math.Abs(pos.X - WorkingArea.X) > WorkingRadius) || (Math.Abs(pos.Y - WorkingArea.Y) > WorkingRadius))
                            return WalkResult.Abort;

                        if (!Parent.Terrain.CanFoilageBePlacedAt(pos.X, pos.Y, plantingType))
                            return WalkResult.NotFound;

                        foilagePos = pos;

                        return WalkResult.Success;
                    }))
                return false;

            // plant new foilage
            JobFoilagePlanting job = new JobFoilagePlanting(SpawnWorker(), foilagePos, this, plantingType);

            job.OnCompleted += (unused, succeeded) => { onCompleted(); };
            WorkerOrNull.Job = job;

            return true;
        }
    }
}
